package com.lemon.boot.common.utils;

import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.enums.RoleKeyEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 李猛
 * @datetime 2024/6/20 15:57
 * @description 类对象
 */
@Slf4j
public final class SecurityUtil {
    //角色前缀
    public static final String ROLE_PREFIX = "ROLE_";

    //权限前缀
    public static final String PERMISSION_PREFIX = "sys:";

    /**
     * 获取角色集合
     *
     * @param authentication
     * @return
     */
    public static List<String> getRole(Authentication authentication) {
        return getAuthority(authentication).stream().filter(item -> item.startsWith(ROLE_PREFIX)).collect(Collectors.toList());
    }

    /**
     * 获取角色集合
     *
     * @param loginUserDetail
     * @return
     */
    public static List<String> getRole(LoginUserDetail loginUserDetail) {
        return getAuthority(loginUserDetail).stream().filter(item -> item.startsWith(ROLE_PREFIX)).collect(Collectors.toList());
    }

    /**
     * 判断是否包含角色
     *
     * @param authentication
     * @param roles
     * @return
     */
    public static boolean hasRole(Authentication authentication, String... roles) {
        if (roles.length == 0) {
            return false;
        }
        List<String> roleList = getRole(authentication);
        if (CollectionUtils.isEmpty(roleList)) {
            return false;
        }
        return Arrays.stream(roles).anyMatch(roleList::contains);
    }

    /**
     * 判断是否包含角色
     *
     * @param loginUserDetail
     * @param roleKeyEnum
     * @return
     */
    public static boolean hasRole(LoginUserDetail loginUserDetail, RoleKeyEnum roleKeyEnum) {
        String roleKey = String.format("%s%s", ROLE_PREFIX, roleKeyEnum.getRoleKey().toUpperCase());
        return getRole(loginUserDetail).stream().anyMatch(roleKey::equals);
    }

    /**
     * 判断当前用户是否包含角色
     *
     * @param roleKeyEnum
     * @return
     */
    public static boolean hasRole(RoleKeyEnum roleKeyEnum) {
        return hasRole(JwtUtil.getCurrentUser(), roleKeyEnum);
    }

    /**
     * 获取权限集合
     *
     * @param authentication
     * @return
     */
    public static List<String> getAuthority(Authentication authentication) {
        return authentication.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 获取权限集合
     *
     * @param loginUserDetail
     * @return
     */
    public static List<String> getAuthority(LoginUserDetail loginUserDetail) {
        return loginUserDetail.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .distinct()
                .collect(Collectors.toList());
    }

    public static List<String> getPermission(Authentication authentication) {
        return getAuthority(authentication).stream().filter(item -> item.startsWith(PERMISSION_PREFIX)).collect(Collectors.toList());
    }

    /**
     * 判断是否包含权限
     *
     * @param authentication
     * @param permissions
     * @return
     */
    public static boolean hasPermission(Authentication authentication, String... permissions) {
        //1.当前用户的权限
        List<String> permissionList = getPermission(authentication);
        if (CollectionUtils.isEmpty(permissionList) || permissions.length == 0) {
            return false;
        }

        //2.正则表达式匹配
        return permissionList.stream()
                .map(item -> String.format("^%s", item.replace("**", ".*")))
                .map(Pattern::compile)
                .anyMatch(pattern -> Arrays.stream(permissions)
                        .anyMatch(permission -> pattern.matcher(permission).matches()));
    }

    /**
     * uri -> permission
     *
     * @param uri
     * @return
     */
    public static String toPermission(String uri) {
        if (!StringUtils.hasText(uri)) {
            return PERMISSION_PREFIX;
        }
        //去 /
        String path = uri.startsWith("/") ? uri.substring(1) : uri;
        return String.format("%s%s", PERMISSION_PREFIX, path.replaceAll("/", ":"));
    }

    /**
     * 根据认证信息获取用户信息
     *
     * @param authentication
     * @return
     */
    public static LoginUserDetail getLoginUserDetail(Authentication authentication) {
        if (authentication == null || authentication.getPrincipal() == null || !(authentication.getPrincipal() instanceof LoginUserDetail)) {
            throw new ArithmeticException("认证失败");
        }
        return (LoginUserDetail) authentication.getPrincipal();
    }

    /**
     * 认证信息
     *
     * @return
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 设置认证信息
     *
     * @param request
     * @param currentUser
     */
    public static void setAuthentication(HttpServletRequest request, LoginUserDetail currentUser) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(currentUser, currentUser.getPassword(), currentUser.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        //异步线程的时候不能把SpringSecurity当前用户信息context带到子线程
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * 用户信息
     *
     * @return
     */
    public static LoginUserDetail getLoginUserDetail() {
        return getLoginUserDetail(getAuthentication());
    }

    /**
     * 匹配注解
     *
     * @param annotations
     * @param annotationClass
     * @return
     */
    public static boolean isAnnotated(Annotation[] annotations, Class<? extends Annotation> annotationClass) {
        return Arrays.stream(annotations).anyMatch(annotation -> annotationClass.isAssignableFrom(annotation.getClass()));
    }

    /**
     * 判断是否是安全注解
     *
     * @param annotations
     * @return
     */
    public static boolean isSecurityAnnotated(Annotation[] annotations) {
        return isAnnotated(annotations, Secured.class) ||
                isAnnotated(annotations, PreFilter.class) ||
                isAnnotated(annotations, PostFilter.class) ||
                isAnnotated(annotations, PreAuthorize.class) ||
                isAnnotated(annotations, PostAuthorize.class) ||
                isAnnotated(annotations, PermitAll.class) ||
                isAnnotated(annotations, DenyAll.class);
    }
}
